The ONE仿真器进行一系列初始化之后,开始仿真,详情可参考博文《main函数剖析》。仿真的核心部分是world.update()
函数,world.update
负责每隔一段时间,处理所有事件,并且更新所有节点。本文讲解The ONE仿真器每隔一段时间都做些什么。
1. The ONE运行机制
The ONE是基于代理离散事件(agent-based discrete event)仿真器。把节点间连接建立UP或DOWN,消息产生、发送、接收都视为事件,The ONE进行一些初始化工作后,开始仿真。每隔一段间(通过在设置文件Scenario.updateInterval
来设置),系统处理每一个到期的事件,并同时更新所有节点。
1.1 main函数
以批处理(batch mode)为例,main函数整个调用过程如下,详情参考博文《main函数剖析》:
main(String[] args) //DTNSim.java
initSettings(confFiles, firstConfIndex); //初始化设置,读入default_settings.txt
new DTNSimTextUI().start();
initModel(); //创建仿真模型
runSim(); //开始仿真
world.update(); //在每个updateInterval,处理所有到期事件
1.2 world.update
world.udate
,每隔updateInterval
处理所有到期事件,并更新所有节点。值得注意的是:每处理一个到期事件,都会更新所有节点。完了之后,处理节点的移动,再次更新所有节点。相关源代码如下:
//World.java
private List<EventQueue> eventQueues; //事件队列链表,用链表将事件队列组织起来
public void update () {
double runUntil = SimClock.getTime() + this.updateInterval; //
/* process all events that are due until next interval update */
setNextEventQueue(); //找到一个事件队列,该事件队列含有本updateInterval可以处理的事件
while (this.nextQueueEventTime <= runUntil) {
simClock.setTime(this.nextQueueEventTime);
ExternalEvent ee = this.nextEventQueue.nextEvent(); //取得事件
ee.processEvent(this); //处理事件,见2
updateHosts(); //update all hosts after every event,见3
setNextEventQueue();
}
moveHosts(this.updateInterval); //Moves all hosts in the world for a given amount of time
simClock.setTime(runUntil);
updateHosts();
}
2. 处理事件
消息的创建、转发、接收,连接的建立、撤消都可以视为事件。值得注意的是:使用MessageEventGenerator
自动创建消息,只有创建消息事件MessageCreateEvent
在world.update
的processEvent
被处理,消息转发、接收则是在相关路由器的update
被处理。我用MessageEventGenerator
产生消息,用静态移动模型(即导入外部数据集,如infocom06
)来仿真,以下是world.update
处理的部分事件:
CONN up @464.0 43<->8
CONN up @464.0 43<->17
CONN down @464.0 43<->17
CONN up @464.0 43<->23
CONN down @464.0 43<->23
CONN down @464.0 43<->44
CONN up @464.0 43<->53
CONN up @464.0 43<->57
CONN down @464.0 43<->61
MSG @464.0 M16 [8->1] size:1016 CREATE
CONN up @465.0 0<->5
CONN down @465.0 0<->5
CONN down @465.0 0<->6
可见,在我的例子中,world.update
只需处理消息创建和连接建立、撤消事件。关于事件处理,可参考博文《消息创建过程》和《连接事件ConnectionEvent读取与处理》。
3. updateHosts
3.1 节点顺序
The ONE仿真器将所有节点组织成一个ArrayList
,依次取出节点做更新。这里取出节点的顺序有两种:其一,按网络地址network address排序的;其二,随机顺序。默认是随机顺序,可在设置文件设置,相关源代码如下:
# 可在设置文件设置
Optimization.randomizeUpdateOrder = true
public static final boolean DEF_RANDOMIZE_UPDATES = true; //默认是随机顺序,World.java
随机的顺序取决于当前的仿真时钟,相关源代码如下:
//World.java
private List<DTNHost> hosts; //indexed by their network address
private ArrayList<DTNHost> updateOrder; //randomized order
//对所有节点进行随机排序,World.java的updateHosts()
Random rng = new Random(SimClock.getIntTime());
Collections.shuffle(this.updateOrder, rng);
3.2 updateHosts
updateHosts
根据节点是否随机顺序,依次更新每一个节点,简化后的源代码如下:
//World.java
private void updateHosts() {
if (this.updateOrder == null) { //indexed by their network address
for (int i=0, n = hosts.size(); i < n; i++) {
hosts.get(i).update(simulateConnections);
}
} else { //random order
for (int i=0, n = hosts.size(); i < n; i++) {
this.updateOrder.get(i).update(simulateConnections);
}
}
if (simulateConOnce && simulateConnections) {
simulateConnections = false;
}
}
simulateConnections
用于标识网络层network layer是否需要被更新,simulateConOnce
标识只更新一次,相关源代码如下:
//World.java
private boolean simulateConnections; //Should network layer be updated too
private boolean simulateConOnce; //Should the connectivity simulation be stopped after one round
public static final String SIMULATE_CON_ONCE_S = "simulateConnectionsOnce";
simulateConOnce = s.getBoolean(SIMULATE_CON_ONCE_S, false); //默认是false,可以在设置文件更改:Optimization.simulateConnectionsOnce
4. DTNHost.update
节点的更新,先判断节点是否处于活动状态,若不是,断开与该节点建立的所有连接;接着,判断是否需要更新网络层;最后,调用路由协议的update
。其源代码如下:
public void update(boolean simulateConnections) {
if (!isRadioActive()) { // Make sure inactive nodes don't have connections
tearDownAllConnections();
return;
}
if (simulateConnections) {
for (NetworkInterface i : net) {
i.update();
}
}
this.router.update();
}